//
//  packet.cpp
//  Wi-Fi Party remade-1
//
//  Created by Vieta Ken on 2021/5/29.
//

#include "packet.hpp"
#include "app.h"
#include <cstdio>

char nil;

auto headerPool=Slab<header>(10);
auto sendBufferPool=Slab<struct send_buffer_size>(10);
auto fromaddrPool=Slab<struct sockaddr_in>(10);
auto receiveBufferPool=Slab<struct receive_buffer_size>(10);

Packet::Packet(char identifier[IDENTIFIER_LENGTH], unsigned int channel, unsigned int index, void* content, unsigned int length){
    this->initialized=created_from_scratch;
    this->header=headerPool.alloc();
    for(int i=0;i<IDENTIFIER_LENGTH;i++){
        this->header->identifier[i]=identifier[i];
    }
    this->header->channel=channel;
    this->header->index=index;

    this->content.addr=content;
    this->content.length=length;
    if(length>10*1024){
        printf("Buffer too long!");
        exit(1);
    }

    this->send_buffer_allocated=false;
}
/*
 Call this when receiving a packet from the internet
 */
Packet::Packet(int socket,char* identifier){
    this->from_addr=fromaddrPool.alloc();
    this->recv_buffer=(char*)receiveBufferPool.alloc();
    this->initialized=from_a_received_packet;
    socklen_t len=sizeof(struct sockaddr_in);
    long received_length=recvfrom(socket, recv_buffer, RECEIVE_BUFFER_LENGTH, 0, (struct sockaddr *)this->from_addr, &len);
    if(received_length>RECEIVE_BUFFER_LENGTH){
        printf("Received a packet too long to receive");
    }
    this->header=(struct header *)recv_buffer;
    if(strcmp(identifier,this->header->identifier)!=0){
	    throw "Not a packet of this app";//TODO: deal with this situation gracefully
    }
    this->content.addr=(char*)recv_buffer+sizeof(struct header);
    this->content.length=(unsigned int)received_length-sizeof(struct header);

    this->send_buffer_allocated=false;
}

void Packet::send(int socket, sockaddr *address){
    if(this->send_buffer_allocated==false){
        //initialize the send_buffer
        this->send_buffer=(char*)sendBufferPool.alloc();
        this->send_buffer_allocated=true;
        memcpy(this->send_buffer, this->header, sizeof(struct header));
        memcpy(this->send_buffer+sizeof(struct header), this->content.addr, this->content.length);
    }
    if(this->initialized==created_from_scratch){
        size_t ret=sendto(socket, this->send_buffer, sizeof(struct header)+this->content.length, 0, address, sizeof(sockaddr));
        if(ret<0){
            printf("Failed to send packet: return code is %zu\n",ret);
            perror("sendto");
            throw "Failed to send packet";
        }
    }else{
        throw "Trying to send a received packet";
    }
}

/*
 Call this to send the ACKnowledge signal
 to show that you've received the packet
 */
void Packet::ack(int socket, sockaddr *address){
    sendto(socket, this->header, sizeof(struct header), 0, address, sizeof(sockaddr));
}

Packet::~Packet(){
    if(this->initialized==created_from_scratch){
        headerPool.release(this->header);
        if(this->send_buffer_allocated==true){
            sendBufferPool.release((send_buffer_size*)this->send_buffer);
        }
    }else if(this->initialized==from_a_received_packet){
        fromaddrPool.release(this->from_addr);
        receiveBufferPool.release((receive_buffer_size*)this->recv_buffer);
    }

}

